resource.AddFile("sound/StargateExtras/ori_beam_firing.wav")

AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")

include("shared.lua")
include("autorun/server/StargateExtras.lua")

local ENERGY_PER_SHOT = 50000
local COOLING_PER_CYCLE = 5
local COOLING_PER_CYCLE_MIN = 2
local HEAT_PER_SHOT = 100
local MAX_HEAT = 300

ENT.cycleInterval = 0.5
ENT.firingDuration = 2
ENT.state = ENT.States.IDLE
ENT.heat = 0

ENT.Damage = {}
ENT.Damage.min = 100
ENT.Damage.current = 1000
ENT.Damage.max = 10000

local firingSoundPath = Sound("StargateExtras/ori_beam_firing.wav")
local defaultModel = "models/Combine_Scanner.mdl"
local lastUseTime = 0

function ENT:SpawnFunction(player, trace)
	if(!trace.Hit) then 
    return 
  end
   
	local cannon = ents.Create(self:GetClass())
	cannon:SetPos(trace.HitPos + trace.HitNormal * cannon:BoundingRadius())
  cannon:SetVar("Owner", player)
	cannon:Spawn()
	cannon:Activate()
   
	undo.Create(self:GetClass())
	undo.AddEntity(cannon)
	undo.SetPlayer(player)
	undo.Finish()
end

-- Sets the gate overloader's model, physics, health, resources, wire inputs/outputs, etc.
function ENT:Initialize()
	if(util.IsValidModel(self.Entity:GetModel()) == false ||
    self.Entity:GetModel() == "models/error.mdl") then
    Msg("Using default model for "..self:GetClass()..".\n")
    self.Entity:SetModel(defaultModel)
      
    if(util.IsValidModel(self.Entity:GetModel()) == false) then
      error("No valid model for "..self:GetClass()..".\n")
    end
  end
   
  self.Entity:SetSolid(SOLID_VPHYSICS)
	self.Entity:PhysicsInit(SOLID_VPHYSICS)
	
  local physics = self.Entity:GetPhysicsObject()
   
	if(physics && physics:IsValid()) then 
    physics:Wake()
  end
   
  -- Set health
  self.Entity:SetMaxHealth(500)
  self.Entity:SetHealth(500)
  self.maxhealth = 500
   
  -- Set resources
  if(self.HasRD) then
    self:AddResource("energy", 0)
    self:AddResource("ZPE", 0)
    self:AddResource("coolant", 0)
  end
    
    -- Set up wire inputs and outputs
	if(self.HasWire) then
		self:CreateWireInputs("Fire") 
		self:CreateWireOutputs("Heat", "Heat Threshold") 
	end
   
  StargateExtras:RegisterEntity(self)
end

function ENT:Setup(damage)
   self.Damage.current = math.Clamp(damage, self.Damage.min, self.Damage.max)
   ENERGY_PER_SHOT = self.Damage.current * 50
end

function ENT:SetState(state)
   self.state = state
   self.Entity:SetNetworkedInt("state", self.state)
end

-- Respond to a given wire input
function ENT:TriggerInput(inputName, inputValue)
   if(inputName == "Fire" && inputValue ~= 0) then
      self:ChargeWeapon()
   end
end

function ENT:ChargeWeapon()
   if(self.state ~= self.States.IDLE && self.state ~= self.States.COOLING) then
      return false
   end
   
   if(self.HasRD) then
      self:AddResource("energy", ENERGY_PER_SHOT)
   
      if(self:GetResource("energy") < ENERGY_PER_SHOT &&
	       self:GetResource("ZPE") < ENERGY_PER_SHOT) then
        return false
      end
   end
   
   self:SetState(self.States.CHARGING)
   
   self.heat = self.heat + HEAT_PER_SHOT
   
   timer.Simple(1, function() 
      self:FireWeapon()
   end)
   
   return true
end

function ENT:FireWeapon()
   if(self.state ~= self.States.CHARGING) then
      return false
   end
   
   local energyConsumed = ENERGY_PER_SHOT
   
   if(self.HasRD) then
      energyConsumed = self:ConsumeResource("energy", ENERGY_PER_SHOT)
      self:AddResource("energy", 0)
      
      if(energyConsumed < ENERGY_PER_SHOT) then
         return false
      end
   end
   
   self:SetState(self.States.FIRING)
   
   self.beam = ents.Create("energy_beam")
   
   if(ValidEntity(self.beam) == false) then
      error("Could not create energy beam.\n")
   end
   
   local Start = {}
   Start.Entity = self.Entity
   Start.offset = self:GetEmitterPos() - self:GetPos()
   
   local Damage = {}
   Damage.amount = self.Damage.current
   Damage.radius = 200
   
   local beamSpeed = 1500
   
   self.beam:Setup(self.Entity, 
                   Start, 
                   Damage, 
                   beamSpeed,
                   self:GetBeamColour(), 
                   "Ori_BeamWeapon")
                   
   self.beam:Spawn()
   self.beam:Activate()
   
   self.beam:EmitSound(firingSoundPath, 100, 100)
   
   local OnFiringEnd = function() 
      self:SetState(self.States.COOLING)
   end
   
   timer.Simple(self.firingDuration, OnFiringEnd, nil)
end

function ENT:Use()
   if(lastUseTime + 1 >= CurTime()) then
      return
   end
   
   lastUseTime = CurTime()
   
   return self:ChargeWeapon()
end

function ENT:Think()
   self:NextThink(CurTime() + self.cycleInterval)
   self:SetOverlayText("")

   if(self.state == self.States.IDLE || self.state == self.States.COOLING) then
      if(self.HasRD) then
         if(self:GetResource("energy") < ENERGY_PER_SHOT) then
            self:SetOverlayText("Requires "..ENERGY_PER_SHOT.." Energy")   
         end
      end
   
      if(self.state == self.States.COOLING) then
         local cooling = COOLING_PER_CYCLE
         
         if(self.HasRD) then
            cooling = math.max(self:ConsumeResource("coolant", COOLING_PER_CYCLE * 10) / 10, 
                               COOLING_PER_CYCLE_MIN)
         end

         cooling = math.ceil(cooling)
         self.heat = self.heat - cooling
         
         if(self.heat <= 0) then
            self.heat = 0
            self:SetState(self.States.IDLE)
         elseif(!CombatDamageSystem && self.heat > MAX_HEAT) then
            self:Destruct()
            return false
         end
      end
   end
   
   self:UpdateWireOutputs()
	return true
end

-- Updates all wire output values
function ENT:UpdateWireOutputs()
  if(!self.HasWire) then
    return
  end
   
  self:SetWire("Heat", self.heat)
  self:SetWire("Heat Threshold", MAX_HEAT)
end

function ENT:OnTakeDamage(damageInfo)  
   if(self.isDestructing == false && self.isCombatSystemEnabled ~= true) then
      self.Entity:TakePhysicsDamage(damageInfo)
      
      self.Entity:SetHealth(self.Entity:Health() - damageInfo:GetDamage())
       
      if(self.Entity:Health() <= 0) then
         self:Destruct()
      end
   end
end

function ENT:Destruct()
  if(self.isDestructing) then
    return
  end
   
  self.isDestructing = true
   
  local blastRadius = 200
  local blastDamage = self.heat + 50
   
  destructEffect = EffectData()
	destructEffect:SetOrigin(self.Entity:GetPos())
  destructEffect:SetRadius(blastRadius / 2)
	destructEffect:SetMagnitude(blastRadius / 10)
	util.Effect("Explosion", destructEffect, true, true)
   
  local owner = self.Entity:GetVar("Owner", self.Entity)
  util.BlastDamage(self.Entity, owner, self.Entity:GetPos(), blastRadius, blastDamage)
    
	self:Remove()
end

function ENT:OnTakeDamage(damageInfo)
   if(ValidEntity(self.beam) &&
      damageInfo:GetInflictor() == self.beam.Laser) then
      damageInfo:SetDamage(0)
   end
end

function ENT:OnRemove()
  StargateExtras:UnregisterEntity(self)
  StarGate.WireRD.OnRemove(self)
end
